home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 August: Tool Chest / Dev.CD Aug 98 TC.toast / Sample Code / Devices / TradDriverLoaderLib1.0b5 / TradDriverLoaderLib.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-07-11  |  22.2 KB  |  777 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        TradDriverLoaderLib.c
  3.  
  4.     Contains:    Implementation for the pseudo-DriverLoaderLib for 'DRVR's.
  5.  
  6.     Written by:    Quinn "The Eskimo!"
  7.  
  8.     Copyright:    © 1996-1997 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     Change History (most recent first):
  11.  
  12.     You may incorporate this sample code into your applications without
  13.     restriction, though the sample code has been provided "AS IS" and the
  14.     responsibility for its operation is 100% yours.  However, what you are
  15.     not permitted to do is to redistribute the source as "DSC Sample Code"
  16.     after having made changes. If you're going to re-distribute the source,
  17.     we require that you make it clear in the source that the code was
  18.     descended from Apple Sample Code, but that you've made changes.
  19. */
  20.  
  21. #include <LowMem.h>
  22. #include <DriverGestalt.h>
  23. #include <TextUtils.h>
  24.  
  25. // Switched from using:
  26. //
  27. //   #include <PLStringFuncs.h>
  28. //
  29. // to using BlockMoveData because it's so hard to get PLstrcpy working
  30. // across a zillion different compilers.  *sigh*
  31.  
  32. #include "TradDriverLoaderLib.h"
  33.  
  34. ///////////////////////////////////////////////////////////////////////////
  35.  
  36. extern pascal SInt16 TradHigherDriverVersion(NumVersion *dv1, NumVersion *dv2)
  37. {
  38.     UInt16 nonRelRev1, nonRelRev2;
  39.  
  40.     if (dv1->majorRev           > dv2->majorRev)        return  1;
  41.     if (dv1->majorRev           < dv2->majorRev)        return -1;
  42.     if (dv1->minorAndBugRev     > dv2->minorAndBugRev)  return  1;
  43.     if (dv1->minorAndBugRev     < dv2->minorAndBugRev)  return -1;
  44.     if (dv1->stage              > dv2->stage)           return  1;
  45.     if (dv1->stage              < dv2->stage)           return -1;
  46.  
  47.     nonRelRev1 = dv1->nonRelRev;
  48.     nonRelRev2 = dv2->nonRelRev;
  49.     
  50.     if (dv1->stage == finalStage) {
  51.         if (dv1->nonRelRev == 0)                 nonRelRev1 = 0xFFFF;
  52.         if (dv2->nonRelRev == 0)                 nonRelRev2 = 0xFFFF;
  53.     }
  54.  
  55.     if (nonRelRev1 > nonRelRev2)                        return  1;
  56.     if (nonRelRev1 < nonRelRev2)                        return -1;
  57.  
  58.     return 0;
  59. }
  60.  
  61.  
  62. ///////////////////////////////////////////////////////////////////////////
  63.  
  64. extern pascal UnitNumber TradHighestUnitNumber(void)
  65.     // See comment in header file.
  66. {
  67.     return ( LMGetUnitTableEntryCount() - 1);
  68. }
  69.  
  70. ///////////////////////////////////////////////////////////////////////////
  71.  
  72. extern pascal Boolean TradDriverGestaltIsOn(DriverFlags flags)
  73.     // See comment in header file.
  74. {
  75.     return ( (flags & kmDriverGestaltEnableMask) != 0 );
  76. }
  77.  
  78. ///////////////////////////////////////////////////////////////////////////
  79.  
  80. static OSErr DriverGestaltOnOff(DriverRefNum refNum, Boolean setIt)
  81.     // This routine is called by TradDriverGestaltOn and
  82.     //  TradDriverGestaltOff to either set or clear the
  83.     //  kmDriverGestaltEnableMask bit in the DCE flags.
  84. {
  85.     OSErr err;
  86.     AuxDCEHandle thisDCE;
  87.     
  88.     // First called TradGetDriverInformation to validate the refNum
  89.     //  and verify that the driver exists.
  90.     err = TradGetDriverInformation(refNum, nil, nil, nil, nil);
  91.     if (err == noErr) {
  92.         thisDCE = (AuxDCEHandle) GetDCtlEntry(refNum);
  93.         if (setIt) {
  94.             (**thisDCE).dCtlFlags |= kmDriverGestaltEnableMask;
  95.         } else {
  96.             (**thisDCE).dCtlFlags &= ~kmDriverGestaltEnableMask;
  97.         }
  98.     }
  99.     
  100.     return (err);
  101. }
  102.  
  103. ///////////////////////////////////////////////////////////////////////////
  104.  
  105. extern pascal OSErr TradDriverGestaltOn(DriverRefNum refNum)
  106.     // See comment in header file.
  107. {
  108.     return ( DriverGestaltOnOff(refNum, true) );
  109. }
  110.  
  111. ///////////////////////////////////////////////////////////////////////////
  112.  
  113. extern pascal OSErr TradDriverGestaltOff(DriverRefNum refNum)
  114.     // See comment in header file.
  115. {
  116.     return ( DriverGestaltOnOff(refNum, false) );
  117. }
  118.  
  119. ///////////////////////////////////////////////////////////////////////////
  120.  
  121. extern pascal OSErr TradOpenInstalledDriver(DriverRefNum refNum, SInt8 ioPermission)
  122.     // See comment in header file.
  123. {
  124.     OSErr                 err;
  125.     Str255                driverName;
  126.     DriverRefNum    realRefNum;
  127.  
  128.     // Check parameters.
  129.     err = noErr;
  130.     if (ioPermission != fsRdWrPerm) {
  131.         err = paramErr;
  132.     }
  133.     
  134.     // Get the name of the driver, then simply open it.
  135.     if (err == noErr) {
  136.         err = TradGetDriverInformation(refNum, nil, nil, driverName, nil);
  137.     }
  138.     if (err == noErr) {
  139.         if ( driverName[0] == 0 ) {
  140.             err = paramErr;
  141.         }
  142.     }
  143.     if (err == noErr) {
  144.         err = OpenDriver(driverName, &realRefNum);
  145.     }
  146.     if (err == noErr) {
  147.         if (realRefNum != refNum) {
  148.             err = paramErr;        // My favourite error code -- at some intrinsic level, every error is a paramErr (-;
  149.         }
  150.     }
  151.     
  152.     return (err);
  153. }
  154.  
  155. ///////////////////////////////////////////////////////////////////////////
  156.  
  157. extern pascal OSErr TradLookupDrivers(UnitNumber beginningUnit,
  158.                                         UnitNumber endingUnit,
  159.                                         Boolean emptyUnits,
  160.                                         ItemCount *returnedRefNums, 
  161.                                         DriverRefNum *refNums)
  162.     // See comment in header file.
  163. {
  164.     OSErr err;
  165.     AuxDCEHandle     *unitTable;
  166.     ItemCount         maxRefNums;
  167.     UnitNumber         currentUnit;
  168.     
  169.     // Sanity check the parameters.
  170.     if ( endingUnit > TradHighestUnitNumber() ) {
  171.         endingUnit = TradHighestUnitNumber();
  172.     }
  173.     err = noErr;
  174.     if ( beginningUnit > TradHighestUnitNumber() ) {
  175.         err = badUnitErr;
  176.     }
  177.     if (err == noErr) {
  178.         if (beginningUnit > endingUnit ) {
  179.             err = paramErr;
  180.         }
  181.     }
  182.  
  183.     // Now do the real work...
  184.     if (err == noErr) {
  185.         unitTable = (AuxDCEHandle *) LMGetUTableBase();
  186.  
  187.         maxRefNums = *returnedRefNums;
  188.         
  189.         // Loop through each unit table entry from beginningUnit to endingUnit inclusive.
  190.         *returnedRefNums = 0;
  191.         currentUnit = beginningUnit;
  192.         while ( currentUnit <= endingUnit ) {
  193.  
  194.             // If we've still got space to return a unit...
  195.             if ( *returnedRefNums < maxRefNums ) {
  196.             
  197.                 // and we're interested in this unit...
  198.                 if (    (emptyUnits && unitTable[currentUnit] == nil) ||
  199.                             (!emptyUnits && unitTable[currentUnit] != nil) ) {
  200.                     
  201.                     // then copy the unit out to the caller's array
  202.                     refNums[*returnedRefNums] = ~currentUnit;
  203.                     *returnedRefNums += 1;
  204.                 }
  205.             }
  206.             currentUnit += 1;
  207.         }
  208.     
  209.     }
  210.     
  211.     return (err);
  212. }
  213.  
  214. ///////////////////////////////////////////////////////////////////////////
  215.  
  216. enum {
  217.     kNoUnitNumber = 0xFFFF
  218. };
  219.  
  220. static UnitNumber IsDriverInstalled(ConstStr255Param name, UnitNumber skipThisUnit)
  221.     // Look through the unit table to see if there is a driver with this name
  222.     //  already installed.  Note that you might consider calling OpenDriver
  223.     //  here, but that would be wrong.  OpenDriver has similar semantics, but
  224.     //  if it fails to find a driver in the unit table it will search the
  225.     //  current resource chain looking for a DRVR resource to install.
  226.     //  Given that it's likely our client has a DRVR resource in their
  227.     //  resource chain ('cause they're messing around trying to install
  228.     //  drivers), and that OpenDriver will install it without detaching
  229.     //  it from the client's resource file, and that the client's
  230.     //  resource file may go away (ie they're a DropMounter-like application
  231.     //  or some INIT running at system startup), this would be bad.
  232. {
  233.     UnitNumber    endingUnit;
  234.     UnitNumber    unit;
  235.     Str255            unitName;
  236.     
  237.     endingUnit = TradHighestUnitNumber();
  238.     
  239.     for (unit = 0; unit <= endingUnit; unit++) {
  240.         if ( TradGetDriverInformation(~unit, nil, nil, unitName, nil) == noErr) {
  241.             if ( unit != skipThisUnit && EqualString(name, unitName, false, true) ) {
  242.                 return (unit);
  243.             }
  244.         }
  245.     }
  246.     
  247.     return (kNoUnitNumber);
  248. }
  249.  
  250. ///////////////////////////////////////////////////////////////////////////
  251.  
  252. enum {
  253.     kMaximumNumberOfUnitTableEntries = 1024,
  254.     // kMaximumNumberOfUnitTableEntries = 8000,
  255.     
  256.     // kMaximumNumberOfUnitTableEntries is documented in Technote
  257.     //  DV 23 "Driver Education" <http://devworld.apple.com/dev/technotes/dv/dv_23.html>
  258.     //  as being the maximum size that the classic Device Manager
  259.     //  would grow the unit table.  In theory, this limits the system
  260.     //  to 128 unit table entries.
  261.     // However the traditional Mac OS is capable of dealing with much more
  262.     //  than 128 units.  In fact, some multi-port serial card vendors
  263.     //  regularly install more.
  264.     // Prior to Mac OS 8, the PCI DriverLoaderLib enforced the 128 limit.
  265.     //  I filed a bug against the PCI DriverLoaderLib to get this limit
  266.     //  raised, and the new limit under Mac OS 8 is 1024.
  267.     // Given that official sanction, I have now raised the standard
  268.     //  limit enforced by this library to 1024.  I supply an alternative
  269.     //  maximum (8000), designed to keep the unit table smaller than 32K.
  270.     //  This is important because many people use 68K word indexing
  271.     //  (ie x(a0,d0.w) to to access the entries.
  272.     // I have tested TradDriverLoaderLib installing up to 500 device
  273.     //  drivers.  
  274.     
  275.     kNumberOfEntriesToGrowUnitTable = 4
  276.     
  277.     // Technote DV 23 "Driver Education"
  278.     //  <http://devworld.apple.com/dev/technotes/dv/dv_23.html>
  279.     //  documents that the system grows the unit table by 4 entries
  280.     //  at a time.
  281. };
  282.  
  283. ///////////////////////////////////////////////////////////////////////////
  284.  
  285. static OSErr GrowUnitTable()
  286.     // This routine grows the unit table by kNumberOfEntriesToGrowUnitTable,
  287.     //  up to a maximum of kMaximumNumberOfUnitTableEntries.  The routine
  288.     //  is guaranteed to grow the table by at least one entry, or fail
  289.     //  with an error.
  290. {
  291.     OSErr        err;
  292.     Ptr         oldTable;
  293.     Ptr         newTable;
  294.     UInt32    oldCount;
  295.     UInt32    newCount;
  296.  
  297.     // Get the info about the old table, and calculate the new table size.    
  298.     oldTable = LMGetUTableBase();
  299.     oldCount = LMGetUnitTableEntryCount();
  300.     newCount = oldCount + kNumberOfEntriesToGrowUnitTable;
  301.  
  302.     // Guard against growing the table too big.    
  303.     err = noErr;
  304.     if (newCount > kMaximumNumberOfUnitTableEntries) {
  305.         err = unitTblFullErr;
  306.     }
  307.     
  308.     // Allocate the new unit table in the system heap.  Note that we
  309.     //  clear the newly allocated memory, so that later on, when we
  310.     //  use this memory as the new unit table, the newly allocated
  311.     //  entries will be empty.
  312.  
  313.     if (err == noErr) {
  314.         newTable = NewPtrSysClear( newCount * sizeof(AuxDCEHandle));
  315.         err = MemError();
  316.     }
  317.  
  318.     // Copy the unit table entries over to the new table and then switch
  319.     //  to that table.  Note that this sequence doesn't disable interrupts,
  320.     //  instead it relies on the fact that programs can't modify the
  321.     //  unit table at interrupt time, and thus we, running at non-interrupt
  322.     //  time, have exclusive write access to the table.
  323.  
  324.     // Note that the sequence of these next few lines is *very* important.
  325.     //  If we did this in any other order, you could get to a situation
  326.     //  where interrupt code might be looking at an inconsistent 
  327.     //  unit table, which would be bad.
  328.     
  329.     // The sequence is:
  330.     //  1. copy the old unit table entries to the new table
  331.     //  2. change the unit table base pointer, so that interrupts
  332.     //         start using the new unit table
  333.     //  3. then change the unit table count, so that we have
  334.     //         more entries available
  335.     
  336.     if (err == noErr) {
  337.         BlockMoveData(oldTable, newTable, oldCount * sizeof(AuxDCEHandle));    // 1.
  338.         LMSetUTableBase(newTable);                                                                                    // 2.
  339.         LMSetUnitTableEntryCount(newCount);                                                                    // 3.
  340.         
  341.         // Now its safe to dispose of the old unit table.
  342.         DisposePtr(oldTable);
  343.     }
  344.  
  345.     return (err);    
  346. }
  347.  
  348. ///////////////////////////////////////////////////////////////////////////
  349.  
  350. static OSErr FindFreeUnitNumber(UnitNumber beginningUnit,
  351.                                 UnitNumber endingUnit, 
  352.                                 UnitNumber *foundUnit)
  353.     // This routine walks the unit table looking for a free
  354.     //  slot.  The slot must be between beginningUnit
  355.     //  and endingUnit.  If endingUnit is greater than
  356.     //  TradHighestUnitNumber(), then we're allowed
  357.     //  to grow the unit table to meet our needs.
  358. {
  359.     OSErr err;
  360.     Boolean found;
  361.     UnitNumber currentUnit;
  362.     UnitNumber trueEndingUnit;
  363.     AuxDCEHandle *unitTable;
  364.     
  365.     unitTable = (AuxDCEHandle *) LMGetUTableBase();
  366.     
  367.     // Find trueEndingUnit, which is the minimum
  368.     //  of endingUnit and the highest unit number.
  369.     trueEndingUnit = endingUnit;
  370.     if ( trueEndingUnit > TradHighestUnitNumber() ) {
  371.         trueEndingUnit = TradHighestUnitNumber();
  372.     }
  373.  
  374.     // Scan through the unit table, starting at beginningUnit
  375.     //  and ending at trueEndingUnit, looking for an
  376.     //  empty slot.
  377.     currentUnit = beginningUnit;
  378.     found = false;
  379.     while (currentUnit <= trueEndingUnit && !found) {
  380.         found = (unitTable[currentUnit] == nil);
  381.         if (!found) {
  382.             currentUnit += 1;
  383.         }
  384.     }
  385.  
  386.     // Finish up.    
  387.     if (found) {
  388.         // We found an empty slot, return it.
  389.         *foundUnit = currentUnit;
  390.         err = noErr;
  391.     } else {
  392.  
  393.         // We didn't find an empty slot.  If we're
  394.         //  allowed to, grow the unit table, otherwise
  395.         //  just return an error.
  396.         
  397.         if (endingUnit > trueEndingUnit) {
  398.             err = GrowUnitTable();
  399.             if (err == noErr) {
  400.                 *foundUnit = trueEndingUnit + 1;
  401.             }
  402.         } else {
  403.             err = unitTblFullErr;
  404.         }
  405.     }
  406.     
  407.     return (err);    
  408. }
  409.  
  410. ///////////////////////////////////////////////////////////////////////////
  411.  
  412. extern pascal OSErr TradInstallDriverFromPtr(DRVRHeaderPtr driver,
  413.                                                 UnitNumber beginningUnit,
  414.                                                 UnitNumber endingUnit,
  415.                                                 DriverRefNum *refNum)
  416.     // See comment in header file.
  417. {
  418.     OSErr err;
  419.     UnitNumber foundUnit;
  420.     AuxDCEHandle theDCE;
  421.     
  422.     // Sanity check parameters.
  423.     err = noErr;
  424.     if ( driver == nil ) {
  425.         err = paramErr;
  426.     }
  427.     if ( beginningUnit > TradHighestUnitNumber() ) {
  428.         err = badUnitErr;
  429.     }
  430.     if ( err == noErr && beginningUnit > endingUnit ) {
  431.         err = paramErr;
  432.     }
  433.     
  434.     // Check whether this driver is already installed.
  435.     if ( err == noErr ) {
  436.         // Check whether it's already installed.
  437.         foundUnit = IsDriverInstalled(&driver->drvrName[0], kNoUnitNumber);
  438.         if (foundUnit != kNoUnitNumber) {
  439.             // Return the refnum of the existing driver to the caller.
  440.             *refNum = ~foundUnit;
  441.             err = dupFNErr;
  442.         }
  443.     }
  444.     
  445.     // Now walk the unit table looking for a free slot.
  446.     if (err == noErr) {
  447.         err = FindFreeUnitNumber(beginningUnit, endingUnit, &foundUnit);
  448.     }
  449.  
  450.     // We've got a free slot, so let's install the device driver.
  451.     //  Note that we use DriverInstallReserveMem, rather than the standard
  452.     //  DriverInstall, so that the DCE is allocated low in the system
  453.     //  heap.  DriverInstallReserveMem was introduced with the 128K ROM.
  454.  
  455.     if (err == noErr) {
  456.         err = DriverInstallReserveMem(driver, ~foundUnit);
  457.     }
  458.     
  459.     // Now do some important tidying up.
  460.     if (err == noErr) {
  461.  
  462.         // Return the refNum to the caller.
  463.         *refNum = ~foundUnit;
  464.  
  465.         theDCE = (AuxDCEHandle) GetDCtlEntry(*refNum);
  466.         
  467.         // Now setup the DCE properly.  There's a whole pile of things we
  468.         //  have to do, mainly because DriverInstall is such a brain-dead
  469.         //  routine.
  470.         
  471.         // First up, DriverInstall seems to ignore the first parameter
  472.         //  passed to it, so we have to blat the pointer to the driver code in
  473.         //  yourself afterwards.
  474.         
  475.         (**theDCE).dCtlDriver = (Ptr) driver;
  476.  
  477.         // Then we have to set up the flags.  We do this by copying the flags
  478.         //  out of the first word of the driver code.  We make sure to clear
  479.         //  the dRAMBased bit because we're actually a pointer-based driver
  480.         //  and DriverInstallReserveMem sets it to provisionally indicate that
  481.         //  we're a handle based driver.  We also set dNeedLock because
  482.         //  we want the the Device Manager to lock down the DCE.
  483.         
  484.         (**theDCE).dCtlFlags = (driver->drvrFlags & ~dRAMBasedMask) | dNeedLockMask;
  485.  
  486.         // There's also a bunch of fields we copy straight across without
  487.         //  any modification.  You might expect DriverInstall to copy
  488.         //  across these fields from the driver header to the DCE, but it doesn't
  489.         //  do that, so we do it ourselves.
  490.  
  491.         (**theDCE).dCtlDelay = driver->drvrDelay;
  492.         (**theDCE).dCtlEMask = driver->drvrEMask;
  493.         (**theDCE).dCtlMenu  = driver->drvrMenu;
  494.  
  495.         // Finally, we lock the DCE.
  496.         // Note that strictly speaking we don't need to HLock the DCE
  497.         //  because the Device Manager will do it when it you open a driver
  498.         //  that has dNeedLock set.  However, we want to
  499.         //  lock it now because DriverInstallReserveMem has just made sure
  500.         //  that the DCE was created low in the system heap, so we might as
  501.         //  well lock it down low rather than let it float.
  502.  
  503.         HLock( (Handle) theDCE );
  504.     }
  505.     
  506.     return (err);
  507. }
  508.  
  509. ///////////////////////////////////////////////////////////////////////////
  510.  
  511. extern pascal OSErr TradInstallDriverFromHandle(DRVRHeaderHandle driver,
  512.                                                 UnitNumber beginningUnit,
  513.                                                 UnitNumber endingUnit,
  514.                                                 DriverRefNum *refNum)
  515.     // See comment in header file.
  516. {
  517.     OSErr err;
  518.     Size  driverSize;
  519.     DRVRHeaderPtr driverPtr;
  520.     
  521.     driverPtr = nil;
  522.     
  523.     err = noErr;
  524.     if (driver == nil || *driver == nil) {
  525.         err = paramErr;
  526.     }
  527.     if (err == noErr) {
  528.         driverSize = GetHandleSize( (Handle) driver );
  529.     }
  530.     if (err == noErr) {
  531.         driverPtr = (DRVRHeaderPtr) NewPtrSys( driverSize );
  532.         err = MemError();
  533.     }
  534.     
  535.     if (err == noErr) {
  536.         // This is *not* a BlockMoveData call. This time, we really are moving code!
  537.         //  I could have put cache flushing code in here, but then I would have
  538.         //  had to check whether it was available or not.
  539.         BlockMove( *driver, driverPtr, driverSize );
  540.         
  541.         err = TradInstallDriverFromPtr(driverPtr, beginningUnit, endingUnit, refNum);
  542.     }
  543.     
  544.     // Clean up.
  545.     if (err != noErr) {
  546.         // We're returning an error.  The API says we should leave the handle untouched,
  547.         //  but we should definitely clean up our new copy of the driver code.
  548.         if (driverPtr != nil) {
  549.             DisposePtr( (Ptr) driverPtr );
  550.         }
  551.     }
  552.     
  553.     return (err);
  554. }
  555.  
  556. ///////////////////////////////////////////////////////////////////////////
  557.  
  558. extern pascal OSErr TradInstallDriverFromResource(SInt16 rsrcID, StringPtr rsrcName,
  559.                                                 UnitNumber beginningUnit,
  560.                                                 UnitNumber endingUnit,
  561.                                                 DriverRefNum *refNum)
  562.     // See comment in header file.
  563. {
  564.     OSStatus err;
  565.     Handle driverHandle;
  566.     
  567.     // Note: We don't care which zone the resource gets loaded, because 
  568.     //  TradInstallDriverFromHandle makes a copy of it anyway.
  569.  
  570.     // Get the resource, using either rsrcID or rsrcName.
  571.     if (rsrcName == nil) {
  572.         driverHandle = Get1Resource('DRVR', rsrcID);
  573.     } else {
  574.         driverHandle = Get1NamedResource('DRVR', rsrcName);
  575.     }
  576.     
  577.     // Set err if we couldn't get the resource.
  578.     if (driverHandle == nil) {
  579.         err = ResError();
  580.         if (err == noErr) {
  581.             err = resNotFound;
  582.         }
  583.     } else {
  584.         // Make sure we're not killed by some clown making the 'DRVR' purgeable.
  585.         HNoPurge(driverHandle);                    
  586.         err = MemError();
  587.     }
  588.     
  589.     // Now install the driver as if we'd got it from a memory handle.    
  590.     if (err == noErr) {
  591.         err = TradInstallDriverFromHandle( (DRVRHeaderHandle) driverHandle, beginningUnit, endingUnit, refNum);
  592.  
  593.         ReleaseResource(driverHandle);
  594.         if (err == noErr) {
  595.             err = ResError();
  596.         }
  597.     }
  598.     
  599.     return (err);
  600. }
  601.  
  602. ///////////////////////////////////////////////////////////////////////////
  603.  
  604. extern pascal OSErr TradGetDriverInformation(DriverRefNum refNum,
  605.                                                 UnitNumber *thisUnit,
  606.                                                 DriverFlags *flags,
  607.                                                 StringPtr name,
  608.                                                 DRVRHeaderPtr *driverHeader
  609.                                                 )
  610.     // See comment in header file.
  611. {
  612.     OSErr err;
  613.     UnitNumber             tmpUnit;
  614.     AuxDCEHandle        tmpDCE;
  615.     DRVRHeaderPtr        tmpHeader;
  616.     DRVRHeaderHandle    tmpDriverHandle;
  617.     
  618.     // Get some initial information.
  619.     tmpUnit = ~refNum;
  620.     
  621.     // Sanity check the refNum parameter.
  622.     err = noErr;
  623.     if (tmpUnit > TradHighestUnitNumber()) {
  624.         err = badUnitErr;
  625.     }
  626.     if (err == noErr) {
  627.         tmpDCE = (AuxDCEHandle) GetDCtlEntry(refNum);
  628.         if ( tmpDCE == nil ) {
  629.             err = unitEmptyErr;
  630.         }
  631.     }
  632.     if (err == noErr) {
  633.         if ( (*tmpDCE == nil) || (GetHandleSize( (Handle) tmpDCE) < sizeof(DCtlEntry)) ) {
  634.             err = dceExtErr;
  635.         }
  636.     }
  637.     
  638.     // Get the information from the DCE.
  639.     if (err == noErr) {
  640.  
  641.         // From the DCE, find the DRVR header.  This can fail for a number of reasons:
  642.         //     1. dCtlDriver is nil
  643.         //     2. the driver is handle based, and the handle's master point is nil
  644.         //     3. the driver is handle based, and the driver's handle is too small
  645.         // In all of these cases, we set tmpHeader to nil, returning nil to our
  646.         // client.
  647.         
  648.         tmpHeader = (DRVRHeaderPtr) (**tmpDCE).dCtlDriver;
  649.         if ( tmpHeader != nil ) {
  650.             if ( ((**tmpDCE).dCtlFlags & dRAMBasedMask) != 0 ) {
  651.  
  652.                 tmpDriverHandle = (DRVRHeaderHandle) tmpHeader;
  653.                 
  654.                 if ( (*tmpDriverHandle != nil) &&
  655.                             (GetHandleSize( (Handle) tmpDriverHandle) >= sizeof(DRVRHeader)) ) {
  656.                     tmpHeader = *tmpDriverHandle;
  657.                 }
  658.             }
  659.         }
  660.         
  661.         // Now copy out the various requested parameters
  662.         if (thisUnit != nil) {
  663.             *thisUnit = tmpUnit;
  664.         }
  665.         if (flags != nil) {
  666.             *flags = (**tmpDCE).dCtlFlags;
  667.         }
  668.         if (name != nil) {
  669.             if ( tmpHeader == nil ) {
  670.                 name[0] = 0;
  671.             } else {
  672.                 BlockMoveData(&tmpHeader->drvrName[0], name, tmpHeader->drvrName[0] + 1);
  673.             }
  674.         }
  675.         if (driverHeader != nil) {
  676.             *driverHeader = tmpHeader;
  677.         }
  678.     }
  679.     
  680.     return (err);
  681. }
  682.  
  683. ///////////////////////////////////////////////////////////////////////////
  684.  
  685. extern pascal OSErr TradRemoveDriver(DriverRefNum refNum, Boolean immediate)
  686.     // See comment in header file.
  687. {
  688.     OSErr                 err;
  689.     DriverFlags     flags;
  690.     DRVRHeaderPtr driverHeader;
  691.  
  692.     // Check parameters.
  693.     err = noErr;
  694.     if (immediate) {
  695.         err = paramErr;
  696.     }
  697.  
  698.     // Get information about the driver we're closing.
  699.     if (err == noErr) {
  700.         err = TradGetDriverInformation(refNum, nil, &flags, nil, &driverHeader);
  701.     }
  702.     if (err == noErr) {
  703.         if ( driverHeader == nil ) {
  704.             err = paramErr;
  705.         }
  706.     }
  707.     
  708.     // If the driver is open, close it.
  709.     if (err == noErr) {
  710.         if ( (flags & dOpenedMask) != 0 ) {
  711.             err = CloseDriver(refNum);
  712.         }
  713.     }
  714.     
  715.     // Now call the system to remove the driver from the unit table.  Note that this
  716.     //  works because of a subtlety in DriverRemove.  If the driver being removed
  717.     //  is a RAM-based driver (which our drivers aren't), DriverRemove will call
  718.     //  ReleaseResource on the dCtlDriver.  We don't want this, so we make our drivers
  719.     //  not RAM-based.
  720.     
  721.     if (err == noErr) {
  722.         err = DriverRemove(refNum);
  723.     }
  724.     
  725.     if (err == noErr) {
  726.         // All is cool, so let's dispose of the code.
  727.         DisposePtr( (Ptr) driverHeader);
  728.     }
  729.     
  730.     return (err);
  731. }
  732.  
  733. ///////////////////////////////////////////////////////////////////////////
  734.  
  735. extern pascal OSErr TradRenameDriver(DriverRefNum refNum, ConstStr255Param newDriverName)
  736.     // See *important* comment in header file.
  737. {
  738.     OSErr             err;
  739.     Str255             driverName;
  740.     DRVRHeaderPtr     driverHeader;
  741.     
  742.     err = noErr;
  743.     if ( newDriverName[0] == 0 ) {
  744.         err = paramErr;
  745.     }
  746.     if (err == noErr) {
  747.         // Get information about the driver we're renaming.
  748.         err = TradGetDriverInformation(refNum, nil, nil, driverName, &driverHeader);
  749.     }
  750.     if (err == noErr) {
  751.         if ( driverHeader == nil ) {
  752.             err = paramErr;
  753.         }
  754.     }
  755.     
  756.     // Now check the name lengths.  See comment in implementation for details.
  757.     if (err == noErr) {
  758.         if ( newDriverName[0] > driverName[0] ) {
  759.             err = paramErr;
  760.         }
  761.     }
  762.     
  763.     // Now check whether the new name is already present in the unit table.
  764.     if (err == noErr) {
  765.         if ( IsDriverInstalled(newDriverName, ~refNum) != kNoUnitNumber ) {
  766.             err = dupFNErr;
  767.         }
  768.     }
  769.     
  770.     // Now copy in the new driver name.
  771.     if (err == noErr) {
  772.         BlockMoveData( newDriverName, &driverHeader->drvrName[0], newDriverName[0] + 1 );
  773.     }
  774.     
  775.     return (err);
  776. }
  777.